// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using Microsoft.NET.Sdk.WorkloadManifestReader;

namespace Microsoft.DotNet.Cli.Commands.Workload.Install;

internal class WorkloadInstallerFactory
{
    public static IInstaller GetWorkloadInstaller(
        IReporter reporter,
        SdkFeatureBand sdkFeatureBand,
        IWorkloadResolver workloadResolver,
        VerbosityOptions verbosity,
        string userProfileDir,
        bool verifySignatures,
        INuGetPackageDownloader nugetPackageDownloader = null,
        string dotnetDir = null,
        string tempDirPath = null,
        PackageSourceLocation packageSourceLocation = null,
        RestoreActionConfig restoreActionConfig = null,
        bool elevationRequired = true,
        bool shouldLog = true)
    {
        dotnetDir = string.IsNullOrWhiteSpace(dotnetDir) ? Path.GetDirectoryName(Environment.ProcessPath) : dotnetDir;
        var installType = WorkloadInstallType.GetWorkloadInstallType(sdkFeatureBand, dotnetDir);

        if (installType == InstallType.Msi)
        {
            if (!OperatingSystem.IsWindows())
            {
                throw new InvalidOperationException(CliCommandStrings.OSDoesNotSupportMsi);
            }
            // TODO: should restoreActionConfig be flowed through to the client here as well like it is for the FileBasedInstaller below?
            return NetSdkMsiInstallerClient.Create(verifySignatures, sdkFeatureBand, workloadResolver,
                nugetPackageDownloader, verbosity, packageSourceLocation, reporter, tempDirPath, shouldLog: shouldLog);
        }

        if (elevationRequired && !WorkloadFileBasedInstall.IsUserLocal(dotnetDir, sdkFeatureBand.ToString()) && !CanWriteToDotnetRoot(dotnetDir))
        {
            throw new GracefulException(CliCommandStrings.InadequatePermissions, isUserError: false);
        }

        userProfileDir ??= CliFolderPathCalculator.DotnetUserProfileFolderPath;

        return new FileBasedInstaller(
            reporter,
            sdkFeatureBand,
            workloadResolver,
            userProfileDir,
            nugetPackageDownloader,
            dotnetDir: dotnetDir,
            tempDirPath: tempDirPath,
            verbosity: verbosity,
            packageSourceLocation: packageSourceLocation,
            restoreActionConfig: restoreActionConfig);
    }

    private static bool CanWriteToDotnetRoot(string dotnetDir = null)
    {
        dotnetDir ??= Path.GetDirectoryName(Environment.ProcessPath);
        try
        {
            var testPath = Path.Combine(dotnetDir, "metadata", Path.GetRandomFileName());
            if (Directory.Exists(Path.GetDirectoryName(testPath)))
            {
                using FileStream fs = File.Create(testPath, 1, FileOptions.DeleteOnClose);
            }
            else
            {
                Directory.CreateDirectory(Path.GetDirectoryName(testPath));
            }
            return true;
        }
        catch (UnauthorizedAccessException)
        {
            return false;
        }
    }
}
